let dataManager = {
	// high-level methods

	// ========= playlist-related =============

	// returns playlistId:string if successful. "" if unsuccessful.
	// done tested production
	createPlaylistWithTitle: (playlistTitle: string) => {
		console.log("dataManager.createPlaylistWithTitle: ", playlistTitle);

		var theNewPlaylist = new dataManager.classes.Playlist();

		theNewPlaylist.title = playlistTitle;

		// 1. create the playlist folder and make sure it doesn't exist already

		return new Promise(resolve => {

			dataManager.createFolderAtPath("playlists/" + theNewPlaylist.uid)
			.then(() => {
				// 2. initialize the info.json data file
				console.log("Folder created. initializing the json file at path " + theNewPlaylist.uid + "/info.json");
				return dataManager.writeDataToFile("playlists/" + theNewPlaylist.uid + "/info.json", JSON.stringify(theNewPlaylist));

			})
			.then(() => {
				console.log("createPlaylistWithTitle has finished!");
				resolve(theNewPlaylist);
			});

		});


	},

	// returns true if successful
	// done untested production
	deletePlaylistById: (playlistId: string) => {

		console.log("dataManager.deletePlaylistById", playlistId);

		// 1. check if folder exists.

		// 2. delete everything in that folder

		// 3. resolve promise
		// return new Promise((resolve, reject) => {

		return dataManager.deleteFileByPath("playlists/" + playlistId);

			// dataManager.dummyData.Playlists.map((playlistInfo, index) => {
			// 	if(playlistInfo.uid == playlistId) {
			// 		dataManager.dummyData.Playlists.splice(index, 1);
			// 		resolve(true);
			// 		return;
			// 	}
			// });

		// 	console.warn("The playlist cannot be found!");
		// 	reject("Playlist not found!");
		// });

	},

	// returns an array of playlistInfo objects, or an empty array if nothing exists.
	// done tested production
	getAllPlaylists: () => {
		console.log("dataManager.getAllPlaylists()");
		// 1. load the current app document folder
		// 2. get all folders
		// 3. filter out invalid folders by looking at the info.json file inside
		// 4. compile everything into an array
		// 5. resolve with the array

		return new Promise(resolve => {

			var playlistIds = [];

			dataManager.getAllFilesFromFolder("playlists/")
			.then((folderContentList) => {

				folderContentList.map((fileInfo) => {

					var playlistId = fileInfo.name;
					// read the json file data here... or call the async method instead?
					playlistIds.push(playlistId);

				});

				console.log("All playlist ids obtained: ", playlistIds);

				// read all the json files
				var promises = playlistIds.map(playlistId => dataManager.getPlaylistInfoById(playlistId));

				Promise.all(promises).then(results => {

					console.log("dataManager.getAllPlaylists: Playlist infos obtained.", results);					

					var sanitizedResults = [];

					results.map((item) => {
						if(item != null)
							sanitizedResults.push(item);
					});


					resolve(sanitizedResults);

				});

			});

		});

		// return new Promise((resolve, reject) => {

		// 	resolve(dataManager.dummyData.Playlists);

		// });

	},

	// returns an array of soundtrackInfo objects
	// done untested production
	getAllItemsInPlaylist: (playlistId: string) => {

		console.log("dataManager.getAllItemsInPlaylist", playlistId);

		var allItemsInPlaylist = [];

		return new Promise((resolve, reject) => {
			// 1. load the info.json of the said playlist
			dataManager.getPlaylistInfoById(playlistId).then((playlistData) => {
				// 2. for each soundtrack item, use the getSoundtrackInfoById method to retrieve the data

				var promises = playlistData.items.map(soundtrackId => dataManager.getSoundtrackInfoById(playlistId, soundtrackId));

				Promise.all(promises).then(results => {

					console.log("dataManager.getAllItemsInPlaylist: Soundtrack infos obtained.", results);

					// go through the results to make sure that there's no blank objects (failures)
					var sanitizedResults = [];

					results.map((item) => {
						if(item != null)	sanitizedResults.push(item);
					});

					resolve(sanitizedResults);

				});

			});
		});

	},

	// returns a playlistInfo object
	// done tested production
	getPlaylistInfoById: (playlistId: string) => {
		console.log("dataManager.getPlaylistInfoById", playlistId);

		// 1. open the info.json in the target folder
		// 2. return the playlist info data

		return new Promise((resolve, reject) => {

			dataManager.readDataFromFile("playlists/" + playlistId + "/info.json")
			.then((rawData) => {
				resolve(JSON.parse(rawData));
			})
			.catch((e) => {
				// unable to load the target data
				resolve(null);
			});

		});

	},

	// returns true when the save is completed.
	// done untested production
	updatePlaylistInfo: (playlistInfo: Object) => {
		console.log("dataManager.updatePlaylistInfo", playlistInfo);
		// 1. locate the info.json file in folder
		// 2. stringify the new object and save it to file

		playlistInfo.modificationTime = new Date().getTime();

		return dataManager.writeDataToFile("playlists/" + playlistInfo.uid + "/info.json", JSON.stringify(playlistInfo));


	},

	// ====== soundtrack-related ======

	// return a generated soundtrackId
	// done untested production
	createSoundtrackInPlaylist: (playlistId: string) => {
		console.log("dataManager.createSoundtrackInPlaylist", playlistId);

		return new Promise((resolve, reject) => {
			// construct the new object
			var newSoundtrackItem = new dataManager.classes.Soundtrack(playlistId);

			// obtain the current playlistInfo
			dataManager.getPlaylistInfoById(playlistId)
			.then((playlistInfo) => {

				// insert the soundtrack id to playlist
				playlistInfo.items.push(newSoundtrackItem.uid);

				// write the file back to info.json
				return dataManager.updatePlaylistInfo(playlistInfo);

			})
			.then(() => {

				// write the soundtrack info .json file
				return dataManager.writeDataToFile("playlists/" + playlistId + "/" + newSoundtrackItem.uid + ".json", 
													JSON.stringify(newSoundtrackItem));

			})
			.then(() => {
				resolve(newSoundtrackItem);
			})
			.catch((e) => {
				reject(e);
			});
		});
	},

	// done untested production
	updateSoundtrackInfo: (soundtrackInfo: Object) => {

		console.log("dataManager.updateSoundtrackInfo: Updating soundtrack using new info ", soundtrackInfo);

		return dataManager.writeDataToFile("playlists/" + soundtrackInfo.playlistId + "/" + soundtrackInfo.uid + ".json", JSON.stringify(soundtrackInfo));

	},

	// done untested production
	getSoundtrackInfoById: (playlistId: string, soundtrackId: string) => {

		// locate the soundtrack.json using playlistId and soundtrackId

		console.log("dataManager.getSoundtrackInfoById: ", soundtrackId);

		return new Promise((resolve, reject) => {

			dataManager.readDataFromFile("playlists/" + playlistId + "/" + soundtrackId + ".json")
			.then(rawData => {
				var soundtrackInfo = JSON.parse(rawData);

				resolve(soundtrackInfo);
			}).catch(e => {
				reject(e);
			});

		});

	},

	// done untested production
	removeSoundtrackFromPlaylist: (playlistId: string, soundtrackId: string) => {

		console.log("dataManager.removeSoundtrackFromPlaylist", playlistId, soundtrackId);

		// locate the soundtrack
		// delete both the music file and the json file

		return new Promise(resolve => {

			dataManager.getPlaylistInfoById(playlistId)
			.then(playlistData => {
				// remove reference of the soundtrack
				var soundtrackIndex = playlistData.items.indexOf(soundtrackId);
				playlistData.items.splice(soundtrackIndex, 1);

				// save the new playlist Info
				return dataManager.updatePlaylistInfo(playlistData);
			})
			.then(() => {
				// remove the soundtrack itself and the .json file
				dataManager.deleteFileByPath("playlists/" + playlistId + "/" + soundtrackId + ".json");
				dataManager.deleteFileByPath("playlists/" + playlistId + "/" + soundtrackId + ".aac");

				resolve(true);
			});

		});

	},

	// retrieves the currently saved user profile data
	// returns a user profile data object
	getUserProfileData: () => {
		console.log("dataManager.getUserProfileData");

		return new Promise(resolve => {
			dataManager.readDataFromFile("user.json")
				.then((rawData) => {
					var userInfo = JSON.parseData(rawData);
					resolve(userInfo);
				})
				.catch((e) => {
					// file doesn't exist yet.
					var userInfo = new dataManager.classes.User();

					resolve(userInfo);
				});
		});

	},

	updateUserProfileData: (userProfileInfo: object) => {

		console.log("dataManager.updateUserProfileData", userProfileInfo);

		return new Promise(resolve => {
			dataManager.writeDataToFile("user.json", JSON.stringify(userProfileInfo))
			.then(() => {
				resolve(true);
			});
		});

	},

	// low-level helpers
	documentFolderPath: "",
	currentUserId: "USER-12345",
	RNFS: null,
	init: function() {
		console.log("dataManager is being initialized.");
		// look up and set the documentFolderPath
		// look up and set the currentUserId

		this.RNFS = require("react-native-fs");
		console.log("RNFS initiated.", this.RNFS);

		// iterate through all key folders
		this.createFolderAtPath("playlists/")
		.then((result) => {
			console.log("Successfully initiated the playlists folder.", result);
		})
		.catch((error) => {
			console.error("Failed to initiate the playlists folder. ", error);
		});
	},

	// tested production.
	generateUIDForObject: (objectType: string) => {

		return objectType.toUpperCase() + '-xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
			        var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
			        return v.toString(16).toUpperCase();
			    });

	},
	// Valid objectTypes: playlist, soundtrack, author

	// FS-related methods
	processPath: (path: string) => {
		if(path.substring(0, 1) != "/") {
			// relative path
			path = dataManager.RNFS.DocumentDirectoryPath + "/" + path;
		}

		return path;
	},

	// done untested production
	writeDataToFile: (path: string, data: string, encoding?: string) => {

		path = dataManager.processPath(path);

		console.log("dataManager.writeDataToFile", path, data);

		if(!encoding)
			encoding = "utf8";

		return dataManager.RNFS.writeFile(path, data, encoding);

	},
	// done untested production
	readDataFromFile: (path: string, encoding?: string) => {

		path = dataManager.processPath(path);

		console.log("dataManager.readDataFromFile", path);

		if(!encoding)	encoding = "utf8";

		return dataManager.RNFS.readFile(path, encoding);

	},

	// done untested production
	checkIfFileExists: (path: string) => {

		path = dataManager.processPath(path);

		console.log("dataManager.checkIfFileExists", path);

		return dataManager.RNFS.exists(path);

	},
	// done untested production
	getAllFilesFromFolder: (path: string) => {

		path = dataManager.processPath(path);

		console.log("dataManager.getAllFilesFromFolder", path);

		return dataManager.RNFS.readDir(path);

		// return new Promise((resolve, reject) => {
		// 	resolve(["PLAYLIST-12345", "PLAYLIST-23456", "PLAYLIST-34567"]);
		// });
	},
	// done untested production
	deleteFileByPath: (path: string) => {

		path = dataManager.processPath(path);

		console.log("dataManager.deleteFileByPath", path);

		return dataManager.RNFS.unlink(path)
			.then(() => {
				console.log("File " + path + " has been deleted.");
			})
			.catch((error) => {
				console.error("File " + path + " could not be deleted.");
			});

		// return new Promise(resolve => resolve(true));
	},
	// done untested production
	createFolderAtPath: (path: string) => {

		path = dataManager.processPath(path);

		console.log("dataManager.createFolderAtPath",path);

		return dataManager.RNFS.mkdir(path);
	},

	// ============== data object constructors =============
	classes: {
		Playlist: function(playlistId?: string) {
			this.uid = playlistId ? playlistId : dataManager.generateUIDForObject("Playlist");
			this.title = "Untitled Playlist";
			this.notes = "";
			this.authorId = dataManager.currentUserId;
			this.creationTime = new Date().getTime();
			this.modificationTime = new Date().getTime();
			this.items = [];
			this.followers = [];
		},
		Soundtrack: function(playlistId: string) {
			this.uid = dataManager.generateUIDForObject("Soundtrack");
			this.title = "Untitled Song";
			this.notes = "";
			this.creationTime = new Date().getTime();
			this.path = dataManager.processPath("playlists/" + playlistId + "/" + this.uid + ".aac");
			this.playlistId = playlistId;
			this.duration = 0;
		},
		User: function(userId?: string) {
			this.uid = userId ? userId : dataManager.generateUIDForObject("User");
			this.name = "Unnamed User";
			this.email = "";
			this.avatarUrl = "";
			this.avatarLocalPath = "";
			this.creationTime = new Date().getTime();
			this.modificationTime = new Date().getTime();
		}
	},

	
dummyData: {
		"Playlists": [
			{
				uid: "PLAYLIST-12345",
				title: "The first playlist",
				notes: "IUPUI Singers",
				authorId: "USER-12345",
				creationTime: new Date().getTime() - (24*3600*1000),
				modificationTime: new Date().getTime(),
				items: [
					"SOUNDTRACK-12345",
					"SOUNDTRACK-23456",
					"SOUNDTRACK-34567",
				],
				followers: [
					"USER-34567",
					"USER-23456"
				]
			},
			]
		}
	// ============== dummyData ================
	// dummyData: {
	// 	"Playlists": [
	// 		{
	// 			uid: "PLAYLIST-12345",
	// 			title: "The first playlist",
	// 			notes: "IUPUI Singers",
	// 			authorId: "USER-12345",
	// 			creationTime: new Date().getTime() - (24*3600*1000),
	// 			modificationTime: new Date().getTime(),
	// 			items: [
	// 				"SOUNDTRACK-12345",
	// 				"SOUNDTRACK-23456",
	// 				"SOUNDTRACK-34567",
	// 			],
	// 			followers: [
	// 				"USER-34567",
	// 				"USER-23456"
	// 			]
	// 		}, {
	// 			uid: "PLAYLIST-23456",
	// 			title: "The second playlist",
	// 			notes: "Perc Ensemble",
	// 			authorId: "USER-23456",
	// 			creationTime: new Date().getTime() - (36*3600*1000),
	// 			modificationTime: new Date().getTime(),
	// 			items: [
	// 				"SOUNDTRACK-12345",
	// 				"SOUNDTRACK-23456",
	// 				"SOUNDTRACK-34567",
	// 			],
	// 			followers: [
	// 				"USER-12345",
	// 				"USER-34567"
	// 			]
	// 		}, {
	// 			uid: "PLAYLIST-34567",
	// 			title: "The third playlist",
	// 			notes: "The Jazz Ensemble",
	// 			authorId: "USER-34567",
	// 			creationTime: new Date().getTime() - (48*3600*1000),
	// 			modificationTime: new Date().getTime(),
	// 			items: [
	// 				"SOUNDTRACK-12345",
	// 				"SOUNDTRACK-23456",
	// 				"SOUNDTRACK-34567",
	// 			],
	// 			followers: [
	// 				"USER-12345",
	// 				"USER-23456"
	// 			]
	// 		}],
	// 	"Soundtracks": [{
	// 		uid: "SOUNDTRACK-12345",
	// 		title: "Amazing Grace",
	// 		notes: "Some notes here",
	// 		creationTime: new Date().getTime() - (64*3600*1000),
	// 		path: "playlistId/soundtrackId.aac",
	// 		playlistId: "PLAYLIST-12345",
	// 		duration: 305
	// 	}, {
	// 		uid: "SOUNDTRACK-23456",
	// 		title: "A Simple Gift",
	// 		notes: "Some notes here",
	// 		creationTime: new Date().getTime() - (72*3600*1000),
	// 		path: "playlistId/soundtrackId.aac",
	// 		playlistId: "PLAYLIST-23456",
	// 		duration: 215
	// 	}, {
	// 		uid: "SOUNDTRACK-34567",
	// 		title: "Sunnyside Blues",
	// 		notes: "Some notes here",
	// 		creationTime: new Date().getTime() - (12*3600*1000),
	// 		path: "playlistId/soundtrackId.aac",
	// 		playlistId: "PLAYLIST-34567",
	// 		duration: 402
	// 	}],
	// 	"Users": []
	// }

};
/*
#Folder Structure

	{AudioUtils.DocumentDirectoryPath}
		/{playlistId}/						Playlist container
		/{playlistId}/info.json 			Playlist Metadata
		/{playlistId}/{soundtrackId}.aac 	Soundtrack file path
		/{playlistId}/{soundtrackId}.json	Soundtrack metadata

Data Structure

Playlist Info
Object {
	uid: string,							Playlist UID
	title: string,							Playlist title
	notes: string,							Playlist notes
	authorId: string,						UID of the author,
	creationTime: number,					Timestamp of creation
	modificationTime: number,				Timestamp of recent modification
	items: Array 							Array of soundtrack UIDs
	followers: Array 						Array of follower UIDs
}

Soundtrack Info
Object {
	uid: string,							Soundtrack UID
	title: string,							Title of the soundtrack
	notes: string,							Notes of the soundtrack
	creationTime: number,					Timestamp of creation
	path: string,							Path of the actual music file
	playlistId: string,						UID of the parent playlist
	duration: number,						Length of the soundtrack in seconds
}

User Info
Object {
	uid: string,							User UID
	name: string,							Display name of the user
	email: string,							Email address of the user
	avatarUrl: string,						URL of the avatar image
	avatarLocalPath: string,				Location of the local cached avatar image
}
*/

export { dataManager };